package es.uc3m.ce.practice1.ex3;

/**
 * Given a float number, return its IEEE 754 standard representation in a string of characters
 * 
 * @author Adrián Gil Moral - 100293146
 * @author Rafael Medina García - 100292314
 */
public class RealNumber {


	/**
	 * Convert the integer part of the number into binary form
	 * @param intNumber integer number to convert
	 * @return Binary representation of the integer part of the number
	 */
	private static String integerToBinary(int intNumber) {
		String binaryNumber = "";
		String result = "";
		// If the number is 0, return 0
		if (intNumber == 0) {
			return "0";
		}
		
		// Convert the number into binary
		while (intNumber >= 1) {
			binaryNumber += intNumber % 2;
			intNumber /= 2;
		}

		// Reverse the order of the digits so the binary number is written
		// correctly
		for (int i = binaryNumber.length() - 1; i >= 0; i--) {
			result += binaryNumber.charAt(i);
		}

		// Return the number in binary
		return result;
	}
	
	/**
	 * Convert the irrational part of the number into binary form
	 * @param irrNumber irrational number to convert
	 * @return Binary representation of the irrational part of the number
	 */
	private static String irrationalToBinary(double irrNumber) {
		String result = "";
		int i = 0;
		
		// Convert to binary
		while (i < (24)) {
			irrNumber *= 2;
			// Bigger than 1
			if (irrNumber > 1) {
				result += "1";
				irrNumber -= 1;
				// Minor than 1
			} else if (irrNumber < 1) {
				result += "0";
				// Equal to 1
			} else {
				result += "1";
				break;
			}
			i++;
		}
		
		// Return the number in binary
		return result;
	}
	
	/**
	 * Convert a floating point number into its binary representation
	 * @param number number to convert
	 * @return Binary representation of the number
	 */
	private static String numToBinary(float number) {
		String result = "";
		
		// Convert the integer part into binary
		result += integerToBinary((int)Math.floor(number));
		
		// Add the separator
		if (number - Math.floor(number) != 0)
			result += ".";
		
		// Convert the irrational part into binary
		result += irrationalToBinary(number - Math.floor(number));
		
		// Return the number in binary
		return result;
		
	}
	
	/**
	 * Normalize the binary number
	 * @param binaryNumber number to normalize
	 * @return Normalization of the binary number
	 */
	private static String normalize(String binaryNumber) {
		String result = "";
		/* Move the decimal separator */
		int posDot = 0;
		int posOne = 0;
		// Find the '.'
		for (int i = 0; i < binaryNumber.length(); i++) {
			if (binaryNumber.charAt(i) == '.') {
				posDot = i;
				break;
			}
		}
		// Find the first 1
		for (int i = 0; i < binaryNumber.length(); i++) {
			if (binaryNumber.charAt(i) == '1') {
				posOne = i;
				break;
			}
		}
		// Move the separator
		// Write until the first 1
		for (int i = 0; i <= posOne; i++) {
			result += String.valueOf(binaryNumber.charAt(i));
		}
		// Add the '.'
		result += ".";
		// Add the rest of the number
		for (int i = posOne + 1; i < posDot; i++) {
			result += String.valueOf(binaryNumber.charAt(i));
		}
		for (int i = posDot + 1; i < binaryNumber.length(); i++) {
			result += String.valueOf(binaryNumber.charAt(i));
		}

		return result;
		
	}
	
	/**
	 * Calculate the exponent of the IEEE 754 number
	 * @param oldDot the previous position of the decimal separator
	 * @param binaryNumber the binary number to calculate the exponent from
	 * @return Exponent of the IEEE 754 number for later transformation into binary
	 */
	private static int getExponent(int oldDot, String binaryNumber) {
		int newDot = 0;
		// Get the position of the separator
		for (int i = 0; i < binaryNumber.length(); i++) {
			if (binaryNumber.charAt(i) == '.') {
				newDot = i;
				break;
			}
		}
		// Get exponent
		return ((oldDot - newDot) + 127);
	}
	
	/**
	 * Round the significant part of the binary number
	 * @param binaryNumber the binary number to round
	 * @return Rounded significant part of the IEEE 754 number
	 */
	private static String roundSignificant(String binaryNumber) {
		// Exclude the decimal separator
		String[] temp = binaryNumber.split("[.]");
		// Get the relevant part
		String[] significant = new String[temp[1].length()];
		for (int i = 0; i < significant.length; i++) {
			significant[i] = String.valueOf(temp[1].charAt(i));
		}
		
		// Find the length of the number
		int length = significant.length;
		
		// Round the number
		if (length > 23) {
			int it = 23;
			boolean rounded = false;
			boolean carry = false;
			// Start rounding
			while (it > 1 && rounded == false) {
				// -- No carry --
				if (significant[it] == "1" && significant[it-1] == "0") {
					significant[it-1] = "1";
					it--;
					rounded = true;
				} else if (significant[it] == "1" && significant[it-1] == "1"){
					significant[it-1] = "0";
					it--;
					carry = true;
				// -- Carry --
				} else if (carry = true && significant[it-1] == "0") {
					significant[it-1] = "1";
					it--;
					carry = false;
					rounded = true;
				} else if (carry = true && significant[it-1] == "1") {
					significant[it-1] = "0";
					carry = true;
					it--;
				}
			}
		}
		
		// Return the number
		String result = "";
		for (int i = 0; i < significant.length; i++) {
			result += significant[i];
		}
		return result;
	}
	
	/**
	 * Add 0 to the significant until there are 23 bits
	 * @param significant the significant to which add 0
	 * @return Final result for the significant
	 */
	private static String addZeros(String significant) {
		// Add 0s to the number until we reach 23 bits
		int bits = significant.length();
		for (int i = bits - 1; i < 22; i++) {
			significant += "0";
		}
		return significant;
	}
	
	/**
	 * Transform a floating point number into IEEE 754 standard representation
	 * @param number the number to transform
	 * @return IEEE 754 standard representation of the given number
	 */
	private static String floatToIEEE754(float number) {
		String ieeeResult = "";
		
		/* Check if the number positive or negative */
		// Negative
		if (number < 0) {
			ieeeResult += "1 ";
		} else {
		// Positive
			ieeeResult += "0 ";
		}
		
		/* Convert the number into binary */
		String binaryNumber = "";
		// Get rid of the sign
		binaryNumber = numToBinary(Math.abs(number));
		// Find the separator to calculate the exponent
		int posDot = 0;
		for (int i = 0; i < binaryNumber.length(); i++) {
			if (binaryNumber.charAt(i) == '.') {
				posDot = i;
				break;
			}
		}
		// Normalize the number
		binaryNumber = normalize(binaryNumber);
		
		/* Calculate the exponent */
		int exponent = getExponent(posDot, binaryNumber);
		// Convert the exponent into binary
		if (exponent == 127) { // If the exponent is 127, we need to add a 0
			ieeeResult += "0" + integerToBinary(exponent) + " ";
		} else {
		ieeeResult += integerToBinary(exponent) + " ";
		}
		
		/* Round the significant */
		binaryNumber = roundSignificant(binaryNumber);
		// Add the significant part to the result
		ieeeResult += addZeros(binaryNumber);
		
		// Return the IEEE 754 representation
		return ieeeResult;
	}
	
	public static void main(String[] args) {
		System.out.println("The IEEE 754 standard representation of 25.6 is:");
		System.out.println(floatToIEEE754((float)25.6));
		System.out.println("The IEEE 754 standard representation of -53.25 is:");
		System.out.println(floatToIEEE754((float)-53.25));
		System.out.println("The IEEE 754 standard representation of 1.5 is:");
		System.out.println(floatToIEEE754((float)1.5));
		System.out.println("The IEEE 754 standard representation of -13.84 is:");
		System.out.println(floatToIEEE754((float)-13.84));
	}
}